package com.final_project.zombiemenace.highscore;

import java.io.File;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

public class HighScoreHandler implements IHighscoreHandler {
	private static final String HIGHSCORES_FILE_NAME = "highscores";
	private static final String DOT = ".";
	private static final String WHITE_SPACE = " ";
	private static final String EMPTY_STRING = "";
	private static final String NEW_LINE = "\n";
	private static final int NUMBER_OF_HIGHSCORES = 10;
	private Context context;
	private SharedPreferences highScores;
	private Editor highscoreEditor;
	private int[] highScorePoints;
	
	public HighScoreHandler(Context context){
		this.context = context;
		highScores = context.getSharedPreferences(HIGHSCORES_FILE_NAME, Context.MODE_PRIVATE);
		highscoreEditor = highScores.edit();
		highScorePoints = new int[NUMBER_OF_HIGHSCORES];
		initializeDefaultHighScoreSharedPreferences();
		putAllHighScoresPointsInArray();		
	}
	
	/**
	 * Initialize shared preference file if it doesn't exist with 
	 * default values.The file doesn't exits if the Applications is not installed
	 * or installed and not started
	 */
	@SuppressLint("SdCardPath")
	private void initializeDefaultHighScoreSharedPreferences(){
		File f = new File("/data/data/" + context.getPackageName() +  "/shared_prefs/highscores.xml");
		if(!f.exists())
		{ 
			highscoreEditor.putString("firstPlace", "1. First Place 0");
			highscoreEditor.putString("secondPlace", "2. Second Place 0");
			highscoreEditor.putString("thirdPlace", "3. Third Place 0");
			highscoreEditor.putString("fourthPlace", "4. Fourth Place 0");
			highscoreEditor.putString("fifthPlace", "5. Fifth Place 0");
			highscoreEditor.putString("sixthPlace", "6. Sixth Place 0");
			highscoreEditor.putString("seventhPlace", "7. Seventh Place 0");
			highscoreEditor.putString("eighthPlace", "8. Eighth Place 0");
			highscoreEditor.putString("ninthPlace", "9. Ninth Place 0");
			highscoreEditor.putString("tenthPlace", "10. Tenth Place 0");
			highscoreEditor.commit();
		}
	}
	
	/**
	 * Gets all highscore strings and then extracts and puts each 
	 * high score points value into an array representing all the 
	 * Highscore points values
	 */
	private void putAllHighScoresPointsInArray(){
		for (int place = 0; place < NUMBER_OF_HIGHSCORES; place++) {
			String highscore = getHighScoreString(place);
			if(highscore.equals(EMPTY_STRING)){
				// Throw exception empty high score String
			}
			highScorePoints[place] = getPointsValueFromString(highscore);
		}
	}
	/* (non-Javadoc)
	 * @see com.final_project.zombiemenace.highscore.IHighscoreHandler#placeNewHighScore(java.lang.String, int)
	 */
	@Override
	public void placeNewHighScore(String name, int score){
		if(shouldBeInHighScore(score)){
			for (int place = 0; place < NUMBER_OF_HIGHSCORES; place++) {
				if(score >= highScorePoints[place]){
					addNewHighScore(name,score, place);
					return;
				}
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see com.final_project.zombiemenace.highscore.IHighscoreHandler#getHighScoresAsString()
	 */
	@Override
	public String getHighScoresAsString(){
		StringBuilder sb = new StringBuilder();
		for (int place = 0; place < NUMBER_OF_HIGHSCORES; place++) {
			String highScore = highScores.getString(getCorrectKey(place), EMPTY_STRING);
			sb.append(highScore + NEW_LINE);
		}
		return sb.toString();
	}
	/**
	 * Rearrange the order of the scores so that new scores push down old scores, so 
	 * the order be logically correct.Collects the Current Score by given place,removes it puts the new score at that place
	 * and then calls  the same function (recursively) with the next place and collected score
	 * @param place - integer value representing the place of the current score
	 * @param replacedScore - String value representing the score information associated with the current place
	 */
	private void rearrangeOrder(int place, String replacedScore) {
		if(place >= NUMBER_OF_HIGHSCORES){
			return;
		}else{
			String correctPositionKey = getCorrectKey(place);
			String currentScoreAtPosition = highScores.getString(
					correctPositionKey, EMPTY_STRING);
			highscoreEditor.remove(correctPositionKey);

			String playerName = getName(replacedScore);
			int playerPoints = getPointsValueFromString(replacedScore);
			String correctedReplacedScore = getCorrectHighScoreValue(place,
					playerName, playerPoints);

			highscoreEditor.putString(correctPositionKey,
					correctedReplacedScore);
			highscoreEditor.commit();
			rearrangeOrder(place + 1, currentScoreAtPosition);
		}
	}


	/**
	 * Adding the new highScore to the list at the correct place, and then 
	 * rearrange order below the  selected score
	 * @param name - String value representing the name of the player scored the points 
	 * @param scoredPoints - integer value representing the points the player has scored
	 * @param place - integer value representing the place, at which the score is
	 */
	private void addNewHighScore(String name, int scoredPoints, int place) {
		String correctHighScoreValue = getCorrectHighScoreValue(place, name, scoredPoints);
		rearrangeOrder(place,correctHighScoreValue);
	}

	/**
	 * Builds and returns a String representing the score of the player with name, points
	 * and place.Place value must be incremented by 1 to be accurate with the scale from 1 to 10
	 * 
	 * @param place - integer value representing the place, at which the score is
	 * @param name - String value representing the name of the player scored the points 
	 * @param scoredPoints - integer value representing the points the player has scored
	 * @return -String value representing the  score  in format 'place'. 'name' 'score'.
	 * For Example "4. ZombieKiller 4300"
	 */
	private String getCorrectHighScoreValue(int place, String name, int scoredPoints) {
		StringBuilder sb = new StringBuilder();
		sb.append((place+1) +DOT + WHITE_SPACE);
		sb.append(name + WHITE_SPACE);
		sb.append(scoredPoints);
		return sb.toString();
	}

	/* (non-Javadoc)
	 * @see com.final_project.zombiemenace.highscore.IHighscoreHandler#shouldBeInHighScore(int)
	 */
	@Override
	public boolean shouldBeInHighScore(int scoredPoints) {
		if(scoredPoints < highScorePoints[NUMBER_OF_HIGHSCORES - 1]){
			return false;
		}
		return true;
	}
	
	
	// !----- Helper Methods -----! 
	
	/**
	 * Define which is the correct key to get or set data through shared preference file
	 * @param key integer value used to determine which is the correct key 
	 * @return - String value representing the correct key value
	 */
	private String getCorrectKey(int key){
		switch(key){
		case 0: return "firstPlace";
		case 1: return "secondPlace";
		case 2: return "thirdPlace";
		case 3: return "fourthPlace";
		case 4: return "fifthPlace";
		case 5: return "sixthPlace";
		case 6: return "seventhPlace";
		case 7: return "eighthPlace";
		case 8: return "ninthPlace";
		case 9: return "tenthPlace";
		default: return EMPTY_STRING;
		}
	}
	
	/**
	 * Gets the string representing the HighScore of current place
	 * @param place - integer value representing the place, at which the score is
	 * @return  String value representing the score
	 */
	private String getHighScoreString(int place){
		String correctKey = getCorrectKey(place);
		if(correctKey == null || correctKey.length() <= 0){
			// Throw exception that the given key is wrong
		}else{
			return highScores.getString(correctKey, EMPTY_STRING);
		}
		return EMPTY_STRING;
	}
	
	/**
	 * Gets the points integer value as a String from given String
	 * @param scoreData - String Value representing the score information
	 * @return - integer value representing the points of the current highscore
	 */
	private int getPointsValueFromString(String scoreData) {
		int scoreValueStartPoint = scoreData.lastIndexOf(' ') + 1;
		try{
			return Integer.parseInt(scoreData.substring(scoreValueStartPoint));
		}catch(NumberFormatException nfe){
			// better exception Handle
			nfe.printStackTrace();
		}
		return 0;
	}
	
	/**
	 * Gets the name of the player scored the points 
	 * @param scoreData - String Value representing the score information
	 * @return - String Value representing the given by player name for the current highscore
	 */
	private String getName(String scoreData){
		int scoreNameEndPoint = scoreData.lastIndexOf(' ');
		int scoreNameStartPoint = scoreData.indexOf('.')+2;
		try{
			return scoreData.substring(scoreNameStartPoint, scoreNameEndPoint);
		}catch(IndexOutOfBoundsException iobe){
			// Better exception Handle
			iobe.printStackTrace();
		}
		return "Unnamed";
	}
}
